package com.kvn.dal.core.retry.beforehand;

import java.lang.reflect.Method;
import java.util.List;

import javax.annotation.Resource;

import org.apache.commons.lang3.ArrayUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import com.kvn.dal.core.dao.IJobBeforehandRetryDao;
import com.kvn.dal.core.pojo.JobBeforehandRetry;
import com.kvn.dal.core.pojo.enums.JobBeforehandRetryStatus;
import com.kvn.dal.core.retry.RetryParam;
import com.kvn.dal.log.Log;

/**
 * 事前重试切面：记录重试记录
 * @author wzy
 * @date 2017年7月14日 下午5:32:12
 */
public class BeforehandRetryAspect {
	private static final Logger logger = LoggerFactory.getLogger(BeforehandRetryAspect.class);

	@Resource
	private IJobBeforehandRetryDao jobBeforehandRetryDao;

	public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
		MethodSignature ms = (MethodSignature) pjp.getSignature();
		Method method = ms.getMethod();
		Object[] args = pjp.getArgs();

		BeforehandRetry brAnno = method.getAnnotation(BeforehandRetry.class);
		boolean isRetryThread = ThreadContext.getContext().isRetryThread(); // 从上下文取
		if (brAnno == null || isRetryThread) { // 不需要拦截
			logger.info("========> 不需要拦截:brAnno={}, isRetryThread={}", brAnno, isRetryThread);
			return pjp.proceed();
		}

		// 记录调用信息
		JobBeforehandRetry jobBeforehandRetry = doBefore(brAnno, method, args);

		Object rlt = null;
		Exception catchedException = null;
		try {
			rlt = pjp.proceed();
		} catch (Exception e) {
			catchedException = e;
			throw e; // 异常执行，终止程序
		} finally {
			// 更新调用信息
			try {
				doAfter(jobBeforehandRetry, brAnno, catchedException);
			} catch (Exception e) { //

			}
		}

		// 执行成功，返回执行结果
		return rlt;
	}

	/**
	 * 更新调用信息
	 * 
	 * @param jobBeforehandRetry
	 * @param brAnno
	 * @param catchedException
	 */
	private void doAfter(JobBeforehandRetry jobBeforehandRetry, BeforehandRetry brAnno, Exception catchedException) {
		final String OP = "BeforehandRetryAspect#doAfter";
		if (catchedException == null) { // 业务执行正常
			int count = jobBeforehandRetryDao.updateStatus(jobBeforehandRetry.getId(), JobBeforehandRetryStatus.BIZ_SUCCESS,
					JobBeforehandRetryStatus.BIZ_INIT, "业务成功[end]", null);
			logger.info(Log.op(OP).msg("业务执行成功，结束").kv("retryId", jobBeforehandRetry.getId()).kv("count", count).toString());
			return;
		}

		String errMsg = catchedException.getMessage().length() > 120 ? catchedException.getMessage().substring(0, 120)
				: catchedException.getMessage();
		if (needRetry(brAnno, catchedException)) {// 需要重试，更新调用记录信息
			int count = jobBeforehandRetryDao.updateStatus(jobBeforehandRetry.getId(), JobBeforehandRetryStatus.RETRY_INIT,
					JobBeforehandRetryStatus.BIZ_INIT, "重试初始->", errMsg);
			logger.info(Log.op(OP).msg("业务执行失败，等待下次重试....").kv("retryId", jobBeforehandRetry.getId()).kv("count", count).toString(),
					catchedException);
		} else {// 不需要重试，更新调用记录信息
			int count = jobBeforehandRetryDao.updateStatus(jobBeforehandRetry.getId(), JobBeforehandRetryStatus.BIZ_FAIL,
					JobBeforehandRetryStatus.BIZ_INIT, "业务失败（不可重试的异常）[end]", errMsg);
			logger.info(Log.op(OP).msg("业务执行失败，等待下次重试....").kv("retryId", jobBeforehandRetry.getId()).kv("count", count).toString(),
					catchedException);
		}
	}

	private boolean needRetry(BeforehandRetry brAnno, Exception catchedException) {
		for (Class<?> expClass : brAnno.retryFor()) {
			if (catchedException.getClass().isAssignableFrom(expClass)) {
				return true;
			}
		}
		return false;
	}

	private JobBeforehandRetry doBefore(BeforehandRetry brAnno, Method method, Object[] args) {
		JobBeforehandRetry record = new JobBeforehandRetry();
		record.setRetryClassName(method.getDeclaringClass().getName());
		record.setRetryMethod(method.getName());
		record.setMaxRetryCount(brAnno.maxRetryCount());
		record.setStatus(JobBeforehandRetryStatus.BIZ_INIT);
		if (ArrayUtils.isNotEmpty(args)) {
			List<RetryParam> retryParamLs = Lists.newArrayList();
			for (Object param : args) {
				retryParamLs.add(RetryParam.createParam(param));
			}
			record.setRetryParams(JSON.toJSONString(retryParamLs));
		}
		record.setFlowPath("业务初始->");
		jobBeforehandRetryDao.add(record);
		return record;
	}

}
